using System;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace Pscx
{
    static class PscxErrorRecord
    {
        public static ErrorRecord GetHostEntryError(string host, Exception innerException)
        {
            string msg = string.Format("Failed to resolve hostname '{0}'.", host);
            Exception exc = new Exception(msg, innerException);

            return new ErrorRecord(exc, "GetHostEntryError", ErrorCategory.ObjectNotFound, host);
        }

        public static ErrorRecord FileNotFoundError(string path)
        {
            string msg = string.Format("Unable to find the file '{0}'.", path);
            Exception exc = new FileNotFoundException(msg, path);

            return FileError(exc, path);
        }

        public static ErrorRecord DirectoryNotFoundError(string path)
        {
            string msg = string.Format("Unable to find the directory '{0}'.", path);
            Exception exc = new DirectoryNotFoundException(msg);

            return FileError(exc, path);
        }

        public static ErrorRecord IsNotReparsePointError(string path)
        {
            string msg = string.Format("The item '{0}' is not a reparse point.", path);
            Exception exc = new ArgumentException(msg);

            return new ErrorRecord(exc, "PathIsNotReparsePoint", ErrorCategory.InvalidArgument, path);
        }

        public static ErrorRecord DirectoryNotEmptyError(string path)
        {
            string msg = string.Format("The directory '{0}' is not empty.", path);
            Exception exc = new FileNotFoundException(msg, path);

            return new ErrorRecord(exc, "DirectoryNotEmpty", ErrorCategory.InvalidArgument, path);
        }

        public static ErrorRecord FileError(Exception ex, string path)
        {
            ErrorCategory errorCategory;

            if ((ex is FileNotFoundException) || (ex is DirectoryNotFoundException))
            {
                errorCategory = ErrorCategory.ObjectNotFound;
            }
            else if ((ex is UnauthorizedAccessException) || (ex is SecurityException))
            {
                errorCategory = ErrorCategory.SecurityError;
            }
            else
            {
                errorCategory = ErrorCategory.NotSpecified;
            }

            return new ErrorRecord(ex, "FileError", errorCategory, path);
        }

        public static ErrorRecord LastWin32Error(string errorId, ErrorCategory category, object target)
        {
            return new ErrorRecord(LastWin32Exception(), errorId, category, target);
        }

        public static ErrorRecord XmlError(Exception exc)
        {
            ErrorCategory errorCategory = ErrorCategory.NotSpecified;

            if(exc is System.Xml.XmlException)
            {
                errorCategory = ErrorCategory.InvalidOperation;
            }

            return new ErrorRecord(exc, "XmlError", errorCategory, null);
        }

        public static ErrorRecord XsltError(Exception exc)
        {
            ErrorCategory errorCategory = ErrorCategory.NotSpecified;

            if(exc is System.Xml.Xsl.XsltException)
            {
                errorCategory = ErrorCategory.InvalidOperation;
            }

            return new ErrorRecord(exc, "XsltError", errorCategory, null);
        }

        public static ErrorRecord PixelFormatError(System.Drawing.Bitmap bmp)
        {
            ArgumentException exc = new ArgumentException("Pixel format " + bmp.PixelFormat.ToString() + " not supported.");
            return new ErrorRecord(exc, "PixelFormatNotSupported", ErrorCategory.InvalidData, bmp);
        }

        public static ErrorRecord InvalidInputError(IEnumerable<Type> expected, object actual)
        {
            const string Comma = ", ";
            StringBuilder sbExpected = new StringBuilder();
            
            foreach(Type t in expected)
            {
                sbExpected.Append(t.Name);
                if (t.IsGenericType)
                {
                    sbExpected.Append("[");
                    Type[] typeArgs = t.GetGenericArguments();
                    foreach (Type typeArg in typeArgs)
                    {
                        sbExpected.AppendFormat("[{0}]", typeArg.Name);
                        sbExpected.Append(Comma);
                    }
                    sbExpected.Length -= (sbExpected.Length >= Comma.Length ? Comma.Length : 0);
                    sbExpected.Append("]");
                }
                sbExpected.Append(Comma);
            }

            if (sbExpected.Length >= Comma.Length)
            {
                sbExpected.Length -= Comma.Length;
            }

            string msg = string.Format(Resources.Shared.InvalidInput, sbExpected, actual.GetType());
            ArgumentException exc = new ArgumentException(msg);

            return new ErrorRecord(exc, "InvalidPipelineObjectType", ErrorCategory.InvalidData, actual);
        }

        public static Exception LastWin32Exception()
        {
            return Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
        }
    }
}
